#include <condition_variable>
#include <functional>
#include <iostream>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
#include <vector>
#include <atomic>

using namespace std;

#define MAX_THREAD_NUM 10
#define THREADPOOL_STOP 0
#define THREADPOOL_RUN 1

template <class T> class ThreadPool
{
  private:
    int thd_num;
    atomic<int> run_flg;
    vector<thread *> worker;
    queue<T> tasks;
    mutex tasks_lock;
    condition_variable cond;
    mutex con_lock;
    function<bool(T &)> handle;

  public:
    explicit ThreadPool(function<bool(T &)> handle_in, int thd_num_in = MAX_THREAD_NUM)
        : thd_num(thd_num_in), run_flg(THREADPOOL_RUN), handle(handle_in)
    {
        for (int i = 0; i < thd_num; i++)
        {
            thread *td = new thread([this] { thread_access(); });
            worker.push_back(td);
        }
    }
    ~ThreadPool()
    {
        run_flg = THREADPOOL_STOP;
        cond.notify_all();
        for (int i = 0; i < thd_num; i++)
        {
            if (worker[i]->joinable())
            {
                worker[i]->join();
            }
            delete worker[i];
        }
    }
    void add_task(T &t)
    {
        tasks_lock.lock();
        tasks.push(t);
        tasks_lock.unlock();
        cond.notify_all();
    }
    bool get_task(T &t)
    {
        tasks_lock.lock();
        if (tasks.empty())
        {
            tasks_lock.unlock();
            return false;
        }
        t = tasks.front();
        tasks.pop();
        tasks_lock.unlock();
        return true;
    }
    void thread_access()
    {
        T tk;
        unique_lock<mutex> lck(con_lock);
        while (run_flg == THREADPOOL_RUN)
        {
            if (!get_task(tk))
            {
                cond.wait(lck);
                continue;
            }
            handle(tk);
        }
    }
};

class task
{
  public:
    int num;
    task(int i) : num(i)
    {
    }
    task()
    {
    }
};

bool run(task &t)
{
    cout << "task:" << t.num << endl;
    return true;
}

void thread_pool_test()
{
    ThreadPool<task> tp(run, 100);
    for (int i = 0; i < 1000; i++)
    {
        task tk(i);
        tp.add_task(tk);
    }
    this_thread::sleep_for(chrono::seconds(10));
}